package visualization;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JComponent;

import odor.Odor;

import structures.Granule;
import structures.Network;

/**
 * Contains Network object within itself, allows for visualization
 * 
 * Handles drawing of Network onto frames
 * 
 * @author pedro
 */
public class JNetwork extends JComponent
{
	private static final long serialVersionUID = -3397405229565046429L;
	
	public static final int COL_START_X = 10;
	public static final int COL_START_Y = 25;
	public static final int COL_WIDTH = 10;
	public static final int COL_OFFSET = 35;
	public static final int GRANULE_RADIUS = 10;
	
	private Network myNetwork;
	private int numColumns;
	private Odor input;
	private double[] output;
	private double myID = -1;
	
	/**
	 * Sets current Odor to given
	 * @param i
	 */
	public void setOdor(Odor i)
	{
		this.input = i;
	}
	
	public void infoDump()
	{
		if (myNetwork != null)
			myNetwork.infoDump();
	}
	
	public void setID(double id)
	{
		this.myID = id;
	}
	
	/**
	 * String representation of Network
	 */
	@Override
	public String toString()
	{
		//TODO This is subject to change
		if (myNetwork == null)
			return this.getID()+"";
		else
			return this.getID()+", Θ = "+myNetwork.getGranularSensitivity();
	}
	
	public int getNumCols()
	{
		return myNetwork.getNumCols();
	}
	
	public void resetGranules()
	{
		myNetwork.resetGranules();
	}
	
	public double[] getOutput()
	{
		return myNetwork.getOutput();
	}
	
	@Override
	public boolean equals(Object o)
	{
		
		if (o instanceof JNetwork)
		{
			JNetwork temp = (JNetwork) o;
			
			if (temp.myID == this.myID)
			{
				return true;
			}
		}
		
		return false;
	}
	
	public double getID()
	{
		//TODO fix this
		if (myID == -1)
		{
			int temp = (int)System.nanoTime()/1000;
			return (temp>0) ? (myID = temp):(myID = temp*-1);
		}
		else
			return myID;
	}
	
	/**
	 * Creates default JNetwork with given Network
	 * @param c - embedded Network obj ref
	 */
	public JNetwork(Network c)
	{
		this.myNetwork = c;
		
		try{ this.myNetwork.initialize(null); } catch (main.InitializationException e ) { e.printStackTrace(); System.exit(0); }
		
		this.numColumns = c.getNumCols();
		output = null;
	}
	
	public JNetwork(int numCols)
	{
		this.myNetwork = new Network(numCols);
		
		try{ this.myNetwork.initialize(null); } catch (main.InitializationException e ) { e.printStackTrace(); System.exit(0); }
		
		this.numColumns = myNetwork.getNumCols();
		output = null;
	}
	
	public double[] fire(Odor input)
	{
		this.input = input;
		return this.output = this.myNetwork.fire(this.input);
	}
	
	/**
	 * Creates JNetwork with given Odor input
	 * @param c - embedded Network object ref.
	 * @param i - Odor input
	 */
	public JNetwork(Network c, Odor i)
	{
		this.myNetwork = c;
		this.numColumns = c.getNumCols();
		input = i;
		output = new double[i.getInputs().length];
	}
	
	/**
	 * Sets the number of columns in Network
	 * @param num
	 */
	public void setNumColumn(int num)
	{
		numColumns = num;
	}
	
	/**
	 * Paints inputs, Network columns, cells, and outputs
	 * 
	 */
	@Override
	public void paintComponent(Graphics g)
	{
		g.setColor(Color.BLACK);
		
		//Draw inputs
		if(this.input != null)
		{
			int z=0;
			for(double d : input.getInputs())
			{
				g.drawString(""+(int)d, COL_START_X+(COL_OFFSET*z), COL_START_Y-3);
				z++;
			}
		}
		
		
		//Draw columns
		for (int x=0; x<this.numColumns; x++)
		{
			g.drawRect(COL_START_X+(COL_OFFSET*x), COL_START_Y, COL_WIDTH, (GRANULE_RADIUS*myNetwork.getGranulesPerColumn()));
		}
		
		//Draw granules
		for (int y=0; y<this.numColumns; y++)
		{
			for (int x=0; x<myNetwork.getGranulesPerColumn(); x++)
			{
				g.drawOval(COL_START_X+(COL_OFFSET*y), COL_START_Y+(GRANULE_RADIUS*x), GRANULE_RADIUS, GRANULE_RADIUS);
			}
		}
		
		//Fill in granule activations
		for (int y=0; y<this.numColumns; y++)
		{
			for (int x=0; x<myNetwork.getGranulesPerColumn(); x++)
			{
				Granule gc = this.myNetwork.getColumn(y).getGranules().get(x);
				
				if (gc != null && gc.isActive())
				{
					g.setColor(Color.GREEN);
					g.fillOval(COL_START_X+(COL_OFFSET*y), COL_START_Y+(GRANULE_RADIUS*x), GRANULE_RADIUS, GRANULE_RADIUS);
				}
				else if (gc !=  null && !gc.isActive())
				{
					g.setColor(Color.RED);
					g.fillOval(COL_START_X+(COL_OFFSET*y), COL_START_Y+(GRANULE_RADIUS*x), GRANULE_RADIUS, GRANULE_RADIUS);
				}
				else
				{
					g.drawOval(COL_START_X+(COL_OFFSET*y), COL_START_Y+(GRANULE_RADIUS*x), GRANULE_RADIUS, GRANULE_RADIUS);
				}
			}
		}
		
		g.setColor(Color.BLACK);
		
		//Draw outputs (if they exist)
		if(this.output != null)
		{
			int z=0;
			for(double d : output)
			{
				g.drawString(""+(int)d, COL_START_X+(COL_OFFSET*z), COL_START_Y+5+(GRANULE_RADIUS*(myNetwork.getGranulesPerColumn()+1)));
				z++;
			}
		}
		
		g.setColor(Color.BLACK);
	}

	public Odor getInput() 
	{
		return this.input;
	}

	public int getGranulesPerColumn() 
	{
		return myNetwork.getGranulesPerColumn();
	}
	
	public static int getGranulesPerColumn(JNetwork n)
	{
		return n.getGranulesPerColumn();
	}
}
